home *** CD-ROM | disk | FTP | other *** search
/ Complete Linux / Complete Linux.iso / docs / system / mail / delivery / deliver.tz / deliver / unctime.y < prev    next >
Encoding:
Text File  |  1992-12-07  |  12.0 KB  |  557 lines

  1. /*
  2.  * $Header: unctime.y,v 2.5 90/05/09 12:25:10 chip Exp $
  3.  *
  4.  * Conversion of ctime-style date string back to a time_t.
  5.  * This module is in a different style from the rest of deliver
  6.  * because Chip Salzenberg didn't write it.
  7.  *
  8.  * $Log:    unctime.y,v $
  9.  * Revision 2.5  90/05/09  12:25:10  chip
  10.  * Use isxxx() and toxxx() portably.  Test for exact match on timezone
  11.  * names.  Terminate token strings properly.  [From G. Paul Ziemba]
  12.  * 
  13.  * Revision 2.4  89/12/19  16:29:49  network
  14.  * Make timezone handling portable to System V.
  15.  * 
  16.  * Revision 2.3  89/12/14  17:33:56  network
  17.  * Fix for Ultrix, which doesn't define DST_CAN.
  18.  * 
  19.  * Revision 2.2  89/10/31  12:15:45  network
  20.  * Fix erroneous cast in BSD-specific code.
  21.  * 
  22.  * Revision 2.1  89/06/09  12:25:42  network
  23.  * Update RCS revisions.
  24.  * 
  25.  * Revision 1.3  89/06/09  12:24:01  network
  26.  * Baseline for 2.0 release.
  27.  * 
  28.  */
  29.  
  30. /* time_t
  31.    unctime(s)
  32.       char *s;
  33.  
  34. Convert s, which may be in almost any reasonable date format, to
  35. a time_t integer suitable for consumption by ctime(3).  Coincidentally
  36. destroys the contents of s.  Return -1 if s is not a recognizable legal date.
  37.  
  38. Any parts of the time left unspecified take on the current values.
  39.  
  40. "4 CST + 23[:10]" adds 23 minutes and optionally 10 seconds to the correction.
  41. "# nnnnnn" forces exactly nnnnnn seconds GMT since Jan. 1, 1970.
  42.   
  43. Copyright 1988, Michael J. Haertel.  Use this routine at your own risk.
  44. You may redistribute verbatim copies of this file.  You may redistribute
  45. modified versions of this file so long as (1) you state who last changed
  46. it, and (2) this copyright notice appears unmodified.
  47.  
  48. Some debugging by W. Anthony Smith.
  49.  
  50. Bug fix, minor enhancements, and non-BSD modifications by David MacKenzie.
  51.  
  52. Several changes by Chip Salzenberg for use with deliver:
  53.     Include "deliver.h".
  54.     Handle military timezones as per RFC822.
  55.     Elimination of "#if FTIME" in favor of "#if !USG".
  56.     Don't modify input string.
  57.     Consider extra junk in date string an error.
  58. */
  59.  
  60. %{
  61. #include "deliver.h"
  62. #include <ctype.h>
  63. #ifdef BSD
  64. # include <sys/time.h>
  65. #else
  66. # include <time.h>
  67. # ifndef USG
  68. #  include <sys/timeb.h>
  69. # endif
  70. #endif
  71.  
  72. extern long atol();
  73.  
  74. /* Delta is correction to turn specified time into GMT. */
  75. /* if (zoneflag), a timezone was explicitly specified. */
  76. static year, month, day, hour, minute, second, delta, zoneflag, errorflag, iflag;
  77. static long iresult;
  78.  
  79. #define YYSTYPE long
  80. %}
  81.  
  82. %token NUM MONTH AM PM
  83.  
  84. %%
  85.  
  86. date:
  87.   day time year
  88.   | day year time
  89.   | time day year
  90.   | time day
  91.   | day time
  92.   | day year
  93.   | day
  94.   | time
  95.   | '#' NUM        { iflag = TRUE; iresult = $2; }
  96.   ;            /* previous line forces exact time in seconds GMT */
  97.  
  98. day:
  99.   NUM MONTH        { month = $2; day = $1; }
  100.   | MONTH NUM        { month = $1; day = $2; }
  101.   | NUM '/' NUM        { month = $1; day = $3; }
  102.   ;
  103.  
  104. year:
  105.   ',' NUM        { year = $2; }
  106.   | '/' NUM        { year = $2; }
  107.   | NUM            { year = $1; }
  108.   ;
  109.  
  110. time:
  111.   clock AM        { hour %= 12; }
  112.   | clock PM        { hour = hour % 12 + 12; }
  113.   | clock
  114.   ;
  115.  
  116. clock:
  117.   NUM ':' NUM ':' NUM    { hour = $1; minute = $3; second = $5; }
  118.   | NUM ':' NUM        { hour = $1; minute = $3; }
  119.   ;
  120.  
  121. %%
  122.  
  123. /* Return 0 if s is the same word as t.
  124.    Return 1 if s is a prefix of t; e.g. prefix("mar", "march") returns 1.
  125.    Return -1 if s is not a prefix of t.
  126.    Note that comparison is case-insensitive. */
  127. static
  128. wordeq(s,t)
  129.      char *s, *t;
  130. {
  131.   while (*s && *t)
  132.     {
  133.       int i, j;
  134.  
  135.       i = *s++;
  136.       j = *t++;
  137.       if (isascii(i) && isupper(i))
  138.     i = tolower(i);
  139.       if (isascii(j) && isupper(j))
  140.     j = tolower(j);
  141.       if (i != j)
  142.     return -1;
  143.     }
  144.  
  145.   if (*s)
  146.     return -1;
  147.   return (*t == 0);
  148. }
  149.  
  150. static char *lexptr;
  151.  
  152. static void
  153. initlex(s)
  154.      char *s;
  155. {
  156.   lexptr = s;
  157. }
  158.  
  159. static char *
  160. months[] =
  161. {
  162.   "jan",
  163.   "feb",
  164.   "mar",
  165.   "apr",
  166.   "may",
  167.   "jun",
  168.   "jul",
  169.   "aug",
  170.   "sep",
  171.   "oct",
  172.   "nov",
  173.   "dec",
  174.   0
  175. };
  176.  
  177. struct zonename
  178. {
  179.   char *name;            /* Name of the time zone. */
  180.   int delta;            /* Correction to add to GMT (in minutes) */
  181. };
  182.  
  183. static struct zonename zones[] =
  184. {
  185.   "gmt", 0,
  186.   "ut",  0,
  187.   "est", -5 * 60,       /* North American time zones */
  188.   "edt", -6 * 60,
  189.   "cst", -6 * 60,
  190.   "cdt", -7 * 60,
  191.   "mst", -7 * 60,
  192.   "mdt", -8 * 60,
  193.   "pst", -8 * 60,
  194.   "pdt", -9 * 60,
  195.   "z",   0,             /* Military time zones */
  196.   "a",   -1 * 60,
  197.   "b",   -2 * 60,
  198.   "c",   -3 * 60,
  199.   "d",   -4 * 60,
  200.   "e",   -5 * 60,
  201.   "f",   -6 * 60,
  202.   "g",   -7 * 60,
  203.   "h",   -8 * 60,
  204.   "i",   -9 * 60,
  205.   "k",   -10 * 60,
  206.   "l",   -11 * 60,
  207.   "m",   -12 * 60,
  208.   "n",   1 * 60,
  209.   "o",   2 * 60,
  210.   "p",   3 * 60,
  211.   "q",   4 * 60,
  212.   "r",   5 * 60,
  213.   "s",   6 * 60,
  214.   "t",   7 * 60,
  215.   "u",   8 * 60,
  216.   "v",   9 * 60,
  217.   "w",   10 * 60,
  218.   "x",   11 * 60,
  219.   "y",   12 * 60,
  220.   0, 0
  221. };
  222.  
  223. /* Lexical analyzer.  Gather alphabetics into tokens; if they are unknown
  224.    strings ignore them, and if they are months return the appropriate value.
  225.    If the token is the name of the time zone set delta = correction and
  226.    zoneflag = TRUE, and skip ahead to the next token (the parser itself
  227.    never sees time zones).
  228.    If the token is a number, return its value.
  229.    If it is a punctuation mark, return the character code.
  230.    Ignore white space.  */
  231. static
  232. yylex()
  233. {
  234.   register i;
  235.   char token[40];    /* Probably paranoid. */
  236.   
  237.   for (;;)
  238.     {
  239.       while (isascii(*lexptr) && isspace(*lexptr))
  240.     lexptr++;
  241.       if (*lexptr == 0)
  242.     return 0;
  243.       else if (isascii(*lexptr) && isalpha(*lexptr))
  244.     {
  245.       i = 0;
  246.       while (isascii(*lexptr) && isalpha(*lexptr))
  247.         token[i++] = *lexptr++;
  248.       token[i] = '\0';
  249.       for (i = 0; months[i]; i++)
  250.         if (wordeq(months[i],token) >= 0)
  251.           {
  252.         yylval = i + 1;
  253.         return MONTH;
  254.           }
  255.       for (i = 0; zones[i].name; i++)
  256.         if (wordeq(zones[i].name,token) == 0)
  257.           {
  258.         int oper, next;
  259.  
  260.         zoneflag = TRUE;
  261.         delta = zones[i].delta;
  262.         oper = yylex();
  263.         /* Syntax: "4 CST + 23[:10]" adds 23 minutes and
  264.         optionally 10 seconds to delta (the correction). */
  265.         if (oper == '+' || oper == '-')
  266.           {
  267.             (void) yylex();
  268.             delta += (oper == '+' ? 60 : -60) * yylval;
  269.             next = yylex();
  270.             if (next == ':')
  271.               {
  272.             (void) yylex();
  273.             delta += (oper == '+' ? 1 : -1) * yylval;
  274.               }
  275.             else
  276.               return next;
  277.           }
  278.         else
  279.           return oper;
  280.           }
  281.       if (wordeq("pm",token) == 0 || wordeq("p.m.", token) == 0)
  282.         return PM;
  283.       if (wordeq("am",token) == 0 || wordeq("a.m.", token) == 0)
  284.         return AM;
  285.       continue;
  286.     }
  287.       else if (isascii(*lexptr) && isdigit(*lexptr))
  288.     {
  289.       i = 0;
  290.       while (isascii(*lexptr) && isdigit(*lexptr))
  291.         token[i++] = *lexptr++;
  292.       token[i] = '\0';
  293.       yylval = atoi(token);
  294.       return NUM;
  295.     }
  296.       else
  297.     return *lexptr++;
  298.     }
  299. }
  300.  
  301. /* ARGSUSED */
  302. static
  303. yyerror(s)
  304.      char *s;
  305. {
  306.   errorflag = TRUE;
  307. }
  308.  
  309. /* Is y a leap year? */
  310. #define leap(y) (((y) % 4 == 0 && (y) % 100 != 0) || (y) % 400 == 0)
  311.  
  312. /* Number of leap years from 1970 to y (not including y itself) */
  313. #define nleap(y) (((y) - 1969) / 4 - ((y) - 1901) / 100 + ((y) - 1601) / 400)
  314.  
  315. /* This macro returns the "day" number of the sunday immediately
  316.    preceding or equal to the argument in the current year. */
  317. #define FIRST_SUNDAY 3
  318. #define dayofepoch(day) ((day) + (year - 1970) * 365 + nleap(year))
  319. #define sunday(day)  ((day) - (dayofepoch(day) + 7 - FIRST_SUNDAY) % 7)
  320.  
  321. /* correction()
  322.    returns the daylight savings correction in seconds to ADD to GMT
  323.    to get correct local time.
  324.    Since we are converting local back to GMT, we SUBTRACT this later on
  325.    (local = gmt + correction(); gmt = local - correction()).
  326.  
  327.    While we're at it, we also add the longitude correction for minutes
  328.    west of Greenwich.  To do this, we have all these fascinating tables
  329.    here . . .  */
  330.  
  331. #ifdef BSD
  332.  
  333. struct dstinfo
  334. {
  335.   int year;            /* Info is for this year, or default if zero. */
  336.   int start;            /* DST begins sunday before this day. */
  337.   int end;            /* DST ends sunday before this day. */
  338. };
  339.  
  340. /* USA. */
  341. static struct dstinfo
  342. usa_dst[] =
  343. {
  344.   1974, 5, 333,
  345.   1975, 58, 303,
  346.   0, 119, 303
  347. };
  348.  
  349. /* Australia. */
  350. static struct dstinfo
  351. aus_dst[] =
  352. {
  353.   1970, 999, 0,
  354.   1971, 303, 0,
  355.   1972, 303, 58,
  356.   0, 303, 65
  357. };
  358.  
  359. /* Western Europe. */
  360. static struct dstinfo
  361. weur_dst[] =
  362. {
  363.   1983, 89, 296,
  364.   0, 89, 303
  365. };
  366.  
  367. /* Middle Europe (also used for Eastern Europe, for lack of better
  368.    information). */
  369. static struct dstinfo
  370. meur_dst[] =
  371. {
  372.   1983, 89, 296,
  373.   0, 89, 272
  374. };
  375.  
  376. /* Canada is same as US, except no early 70's insanity. */
  377. #ifdef DST_CAN
  378. static struct dstinfo
  379. can_dst[] =
  380. {
  381.   0, 119, 303
  382. };
  383. #endif
  384.  
  385. struct dst_rules
  386. {
  387.   int magic;            /* Gettimeofday magic number for rule type */
  388.   struct dstinfo *entry;    /* Pointer to struct dstinfo array. */
  389.   int correction;        /* Correction in minutes to GMT. */
  390. };
  391.  
  392. static struct dst_rules
  393. dstrules[] =
  394. {
  395.   DST_USA, usa_dst, 60,
  396.   DST_AUST, aus_dst, -60,    /* Southern hemisphere */
  397.   DST_WET, weur_dst, 60,
  398.   DST_MET, meur_dst, 60,
  399.   DST_EET, meur_dst, 60,
  400. #ifdef DST_CAN
  401.   DST_CAN, can_dst, 60,
  402. #endif
  403.   -1, 0, 0
  404. };
  405.  
  406. static
  407. correction(day,tz)
  408.      int day;                /* Day number in current year.  */
  409.      struct timezone *tz;
  410. {
  411.   int i, correc = 0;
  412.   struct dstinfo *dst;
  413.   
  414.   /* Did the user specify in the input string a timezone correction to use? */
  415.   if (zoneflag)
  416.     return delta * 60;
  417.  
  418.   /* Since no correction was explicitly specified, we use local time zone and
  419.      DST, as returned by gettimeofday() earlier . . . */
  420.   if (tz->tz_dsttime)
  421.     for (i = 0; dstrules[i].magic != -1; i++)
  422.       if (dstrules[i].magic == tz->tz_dsttime)
  423.     {
  424.       dst = dstrules[i].entry;
  425.       while (dst->year != year && dst->year)
  426.         dst++;
  427.       if (sunday(dst->start) <= day && day <= sunday(dst->end)
  428.           /* For some reason, DST starts/ends at 2 am sunday mornings. */
  429.           && !(day == sunday(dst->start) && hour < 2)
  430.           && !(day == sunday(dst->end) && hour >= 2))
  431.         correc = dstrules[i].correction;
  432.       break;
  433.     }
  434.   correc -= tz->tz_minuteswest;
  435.   return correc * 60;
  436. }
  437.  
  438. #else /* !BSD */
  439.  
  440. static
  441. correction()
  442. {
  443. #ifdef USG
  444.   extern long timezone;
  445. #else
  446.   struct timeb tb;
  447. #endif
  448.   
  449.   /* Did the user specify in the input string a timezone correction to use? */
  450.   if (zoneflag)
  451.     return delta * 60;
  452.  
  453.   /* Since no correction was explicitly specified, we use local time zone. */
  454. #ifdef USG
  455.   return (int) -timezone;
  456. #else
  457.   ftime(&tb);
  458.   return tb.timezone * -60;
  459. #endif
  460. }
  461.  
  462. #endif /* !BSD */
  463.  
  464. static short
  465. monthlens[] =
  466. {
  467.   31,                /* January */
  468.   28,                /* February */
  469.   31,                /* March */
  470.   30,                /* April */
  471.   31,                /* May */
  472.   30,                /* June */
  473.   31,                /* July */
  474.   31,                /* August */
  475.   30,                /* September */
  476.   31,                /* October */
  477.   30,                /* November */
  478.   31                /* December */
  479. };
  480.  
  481. time_t
  482. unctime(s)
  483.      char *s;
  484. {
  485. #ifdef BSD
  486.   struct timeval tv;
  487.   struct timezone tz;
  488. #else
  489.   time_t now;
  490. #endif
  491.   struct tm *tm;
  492.   int dayofyear;
  493.  
  494. #ifdef BSD
  495.   (void) gettimeofday(&tv,&tz);
  496.   /* The cast is required to shut lint up.  Berkeley goes to all the effort
  497.      to define time_t, why don't they use it? */
  498.   tm = localtime((time_t *) &tv.tv_sec);
  499. #else
  500.   (void) time(&now);
  501.   tm = localtime(&now);
  502. #endif
  503.   year = tm->tm_year;
  504.   month = tm->tm_mon + 1;
  505.   day = tm->tm_mday;
  506.   hour = tm->tm_hour;
  507.   minute = tm->tm_min;
  508.   second = tm->tm_sec;
  509.   zoneflag = FALSE;
  510.   errorflag = FALSE;
  511.  
  512.   initlex(s);
  513.   (void) yyparse();
  514.  
  515.   if (errorflag)
  516.     return -1;
  517.  
  518.   /* If garbage beyond valid date, that's an error. */
  519.   while (isascii(*lexptr) && isspace(*lexptr))
  520.     ++lexptr;
  521.   if (*lexptr)
  522.      return -1;
  523.  
  524.   /* User forced the exact time in seconds GMT, no further work necessary. */
  525.   if (iflag)
  526.     return iresult;
  527.  
  528.   /* Try to keep the year reasonable (i.e., within the domain of ctime()). */
  529.   if (year < 1970)
  530.     year += 1900;
  531.   if (year < 1970)
  532.     year += 100;
  533.  
  534.   /* Check for preposterous months/days/times. */
  535.   if (month < 1 || month > 12 || day < 1 ||
  536.       day > monthlens[month - 1] && !(month == 2 && day == 29 && leap(year))
  537.       || hour > 23 || minute > 59 || second > 59)
  538.     return -1;
  539.  
  540.   /* Mostly for convenience in sunday() macro, we use zero-origin days. */
  541.   dayofyear = day - 1;
  542.   if (month > 2 && leap(year))
  543.     ++dayofyear;
  544.   while (--month > 0)
  545.     dayofyear += monthlens[month - 1];
  546.  
  547.   /* Wow! */
  548.   return 86400 * (dayofyear + 365 * (year - 1970) + nleap(year))
  549.     + 3600 * hour + 60 * minute + second
  550. #ifdef BSD
  551.     - correction(dayofyear,&tz)
  552. #else
  553.     - correction()
  554. #endif
  555.     ;
  556. }
  557.